﻿using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using Newtonsoft.Json.Linq;
using TrinityAPI.Base;
using TrinityAPI.Base.Tools;
using TrinityAPI.Base.Models;
using Microsoft.Reporting.WebForms;
using PdfSharp.Pdf;
using PdfSharp.Drawing;

namespace Express.ShunFeng
{
    public class GetWaybill : IAction
    {
        private string courierCode;

        public JToken Invoke(JObject dataset, params JToken[] args)
        {
            var request = dataset.GetJsonObject("request");
            var constant = dataset.GetJsonObject("constant");
            var courierInfo = constant.GetNecessaryJsonObject("courierInfo", "constant");

            courierCode = request.GetNecessaryJsonObjectStringValue("courierCode", "request");
            var config = courierInfo.GetNecessaryJsonObject(courierCode, "constant.courierInfo");
            int apiType = config.GetJsonObjectIntValue("apiType", 0);

            //var response = SearchWaybill(request, config);
            //if (response == null)
            var response = apiType == 0 ? CreateWaybill(request, config) : CreateWaybillNew(request, config);
            return response;
        }

        public JObject SearchWaybillNew(JObject request, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string url = config.GetNecessaryJsonObjectStringValue("url", path);
            string appkey = config.GetNecessaryJsonObjectStringValue("appkey", path);
            string appsecret = config.GetNecessaryJsonObjectStringValue("appsecret", path);
            var timeout = config.GetJsonObjectIntValue("timeout", 30000);
            var proxy = config.GetJsonObjectValue("proxy");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");
            string deliverOrderId = request.GetJsonObjectStringValue("deliverOrderId", orderId);

            JObject json = new JObject();
            json.Add("orderId", deliverOrderId);
            json.Add("searchType", "1");
            json.Add("language", "zh_CN");

            string serviceCode = "EXP_RECE_SEARCH_ORDER_RESP";
            string timestamp = GetTimeStamp(DateTime.Now).ToString();
            string msgData = ToUTF8(json.ToString(Newtonsoft.Json.Formatting.None));
            string msgDigest = Utils.MD5(UrlEncode(msgData + timestamp + appsecret), Encoding.UTF8, false, true);
            string param = string.Format("partnerID={0}&requestID={1}&serviceCode={2}&timestamp={3}&msgData={4}&msgDigest={5}",
                appkey,
                Guid.NewGuid().ToString("N"),
                serviceCode,
                timestamp,
                UrlEncode(msgData),
                msgDigest);

            try
            {
                LogHelper.Info("url: " + url, "SFEApiLog");
                LogHelper.Info("request: " + param, "SFEApiLog");
                var headers = new System.Collections.Generic.Dictionary<string, string>() { { "Content-Type", "application/x-www-form-urlencoded;charset=UTF-8" } };
                string result = HttpHelper.HttpWebRequest(url, param, "POST", timeout, headers, proxy?.ToString());
                LogHelper.Info("response: " + result, "SFEApiLog");

                var responseObject = JSONHelper.JsonToJObject(result);

                string code = responseObject.GetJsonObjectStringValue("apiResultCode");

                if (code.ToUpper() == "A1000")
                {
                    string apiResultData = responseObject.GetJsonObjectStringValue("apiResultData");
                    var apiResultObject = JSONHelper.JsonToJObject(apiResultData);
                    if (!apiResultObject.GetJsonObjectBooleanValue("success"))
                    {
                        throw ExceptionHelper.GenException("@E210000002", apiResultObject.GetJsonObjectStringValue("errorMsg"));
                    }
                    JObject msgDataObject = apiResultObject.GetJsonObject("msgData");
                    JArray waybillNoInfoList = msgDataObject.GetJsonObjectValue("waybillNoInfoList") as JArray;
                    JObject waybillNoInfo = waybillNoInfoList != null && waybillNoInfoList.HasValues ? waybillNoInfoList[0] as JObject : null;
                    msgDataObject["waybillCode"] = waybillNoInfo.GetJsonObjectStringValue("waybillNo");

                    JObject waybill = new JObject();
                    waybill["origincode"] = msgDataObject.GetJsonObjectStringValue("originCode");
                    waybill["destcode"] = msgDataObject.GetJsonObjectStringValue("destCode");
                    waybill["waybillCode"] = waybillNoInfo.GetJsonObjectStringValue("waybillNo");

                    JObject response = new JObject();
                    response["orderId"] = orderId;
                    response["waybillCode"] = waybill["waybillCode"];
                    response["waybillFilePath"] = CreateWaybillPDFNew(request, msgDataObject, config).GetJsonObjectStringValue("waybillFilePath");
                    return response;
                }
                else
                {
                    return null;
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng SearchWaybillNew fail", ex, "SFEApiLog");
                throw new Exception("@E210000001");
            }
        }

        public JObject CreateWaybillNew(JObject request, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string url = config.GetNecessaryJsonObjectStringValue("url", path);
            string appkey = config.GetNecessaryJsonObjectStringValue("appkey", path);
            string appsecret = config.GetNecessaryJsonObjectStringValue("appsecret", path);
            string customerCode = config.GetNecessaryJsonObjectStringValue("customercode", path);
            int expressType = 0;
            int.TryParse(config.GetJsonObjectStringValue("expresstype", "2"), out expressType);
            int payMethod = 0;
            int.TryParse(config.GetJsonObjectStringValue("paymethod", "1"), out payMethod);
            int isDoCall = 0;
            int.TryParse(config.GetJsonObjectStringValue("isdocall", "1"), out isDoCall);
            var timeout = config.GetJsonObjectIntValue("timeout", 30000);
            var proxy = config.GetJsonObjectValue("proxy");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");
            string deliverOrderId = request.GetJsonObjectStringValue("deliverOrderId", orderId);

            JArray cargoDetails = new JArray();
            JArray contactInfoList = new JArray();
            contactInfoList.Add(new JObject()
            {
                { "contactType", 1 },
                { "country", "CN" },
                { "address", request.GetJsonObjectStringValue("from_address") },
                { "company", request.GetJsonObjectStringValue("from_company") },
                { "contact", request.GetJsonObjectStringValue("from_contact") },
                { "mobile", request.GetJsonObjectStringValue("from_mobile") },
                { "tel", request.GetJsonObjectStringValue("from_phone") }
            });
            contactInfoList.Add(new JObject()
            {
                { "contactType", 2 },
                { "country", "CN" },
                { "address", request.GetJsonObjectStringValue("to_address") },
                { "company", request.GetJsonObjectStringValue("to_company") },
                { "contact", request.GetJsonObjectStringValue("to_contact") },
                { "mobile", request.GetJsonObjectStringValue("to_mobile") },
                { "tel", request.GetJsonObjectStringValue("to_phone") }
            });

            var items = request.GetJsonObjectList<JObject>("cargoes");
            if (items != null)
            {
                foreach (var item in items)
                {
                    cargoDetails.Add(new JObject()
                    {
                        { "name", item.GetJsonObjectStringValue("sku") },
                        { "count", item.GetJsonObjectIntValue("qty") }
                    });
                }
            }

            JObject json = new JObject();
            json.Add("orderId", deliverOrderId);
            json.Add("monthlyCard", customerCode);
            json.Add("isDocall", isDoCall);
            json.Add("expressTypeId", expressType);
            json.Add("payMethod", payMethod);
            json.Add("remark", request.GetJsonObjectStringValue("remark"));
            json.Add("language", "zh_CN");
            json.Add("contactInfoList", contactInfoList);
            json.Add("cargoDetails", cargoDetails);

            string serviceCode = "EXP_RECE_CREATE_ORDER";
            string timestamp = GetTimeStamp(DateTime.Now).ToString();
            string msgData = ToUTF8(json.ToString(Newtonsoft.Json.Formatting.None));
            string msgDigest = Utils.MD5(UrlEncode(msgData + timestamp + appsecret), Encoding.UTF8, false, true);
            string param = string.Format("partnerID={0}&requestID={1}&serviceCode={2}&timestamp={3}&msgData={4}&msgDigest={5}",
                appkey,
                Guid.NewGuid().ToString("N"),
                serviceCode,
                timestamp,
                UrlEncode(msgData),
                msgDigest);

            try
            {
                LogHelper.Info("url: " + url, "SFEApiLog");
                LogHelper.Info("request: " + param, "SFEApiLog");
                var headers = new System.Collections.Generic.Dictionary<string, string>() { { "Content-Type", "application/x-www-form-urlencoded;charset=UTF-8" } };
                string result = HttpHelper.HttpWebRequest(url, param, "POST", timeout, headers, proxy?.ToString());
                LogHelper.Info("response: " + result, "SFEApiLog");

                var responseObject = JSONHelper.JsonToJObject(result);

                string code = responseObject.GetJsonObjectStringValue("apiResultCode");

                if (code.ToUpper() == "A1000")
                {
                    string apiResultData = responseObject.GetJsonObjectStringValue("apiResultData");
                    var apiResultObject = JSONHelper.JsonToJObject(apiResultData);
                    if (!apiResultObject.GetJsonObjectBooleanValue("success"))
                    {
                        JObject waybillInfo = null;
                        if (apiResultObject.GetJsonObjectStringValue("errorCode") == "8016") //订单号重复
                        {
                            waybillInfo = SearchWaybillNew(request, config);
                        }
                        if (waybillInfo != null)
                        {
                            return waybillInfo;
                        }
                        throw ExceptionHelper.GenException("@E210000002", apiResultObject.GetJsonObjectStringValue("errorMsg"));
                    }
                    JObject msgDataObject = apiResultObject.GetJsonObject("msgData");
                    JArray waybillNoInfoList = msgDataObject.GetJsonObjectValue("waybillNoInfoList") as JArray;
                    JObject waybillNoInfo = waybillNoInfoList != null && waybillNoInfoList.HasValues ? waybillNoInfoList[0] as JObject : null;
                    msgDataObject["waybillCode"] = waybillNoInfo.GetJsonObjectStringValue("waybillNo");

                    JObject waybill = new JObject();
                    waybill["origincode"] = msgDataObject.GetJsonObjectStringValue("originCode");
                    waybill["destcode"] = msgDataObject.GetJsonObjectStringValue("destCode");
                    waybill["waybillCode"] = waybillNoInfo.GetJsonObjectStringValue("waybillNo");

                    JObject response = new JObject();
                    response["orderId"] = orderId;
                    response["waybillCode"] = waybill["waybillCode"];
                    response["waybillFilePath"] = CreateWaybillPDFNew(request, msgDataObject, config).GetJsonObjectStringValue("waybillFilePath");
                    return response;
                }
                else
                {
                    string msg = responseObject.GetJsonObjectStringValue("apiErrorMsg");
                    throw ExceptionHelper.GenException("@E210000002", msg);
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng CreateFQWaybillNew fail", ex, "SFEApiLog");
                throw new Exception("@E210000001");
            }
        }

        public JObject CreateWaybillPDFNew(JObject request, JObject waybill, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string url = config.GetNecessaryJsonObjectStringValue("url", path);
            string appkey = config.GetNecessaryJsonObjectStringValue("appkey", path);
            string appsecret = config.GetNecessaryJsonObjectStringValue("appsecret", path);
            string waybillFileDir = config.GetNecessaryJsonObjectStringValue("waybillfiledir", path);
            string waybillFileUrl = config.GetJsonObjectStringValue("waybillfileurl");
            string version = config.GetJsonObjectStringValue("cloudPrintVersion");
            string templateCode = config.GetJsonObjectStringValue("templateCode", "fm_76130_standard_BYMY8UF78Q5");
            var timeout = config.GetJsonObjectIntValue("timeout", 30000);
            var proxy = config.GetJsonObjectValue("proxy");

            int packageNo = request.GetJsonObjectIntValue("package_no");
            int packageTotal = request.GetJsonObjectIntValue("package_total");
            string ecomOrderId = request.GetJsonObjectStringValue("ecom_orderid");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");

            string from_company = "******";
            string from_contact = request.GetJsonObjectStringValue("from_contact");
            //string from_phone = null;
            string from_mobile = request.GetJsonObjectStringValue("from_mobile");
            string from_province = request.GetJsonObjectStringValue("from_province");
            string from_city = request.GetJsonObjectStringValue("from_city");
            string from_county = request.GetJsonObjectStringValue("from_county");
            string from_address = from_company;
            string to_company = request.GetJsonObjectStringValue("to_company");
            string to_contact = request.GetJsonObjectStringValue("to_contact");
            string to_phone = request.GetJsonObjectStringValue("to_phone");
            string to_mobile = request.GetJsonObjectStringValue("to_mobile");
            string to_address = request.GetJsonObjectStringValue("to_address");

            string originCode = waybill.GetNecessaryJsonObjectStringValue("originCode");
            string destCode = waybill.GetNecessaryJsonObjectStringValue("destCode");
            string waybillCode = waybill.GetNecessaryJsonObjectStringValue("waybillCode");

            string cargoStr = "";
            var items = request.GetJsonObjectList<JObject>("cargoes");
            if (items != null)
            {
                foreach (var item in items)
                {
                    cargoStr += string.Format("{0}*{1};", item.GetJsonObjectStringValue("sku"), item.GetJsonObjectIntValue("qty"));
                }
            }
            cargoStr = cargoStr + ecomOrderId;

            // 在remark显示(多包裹)
            if (packageTotal > 1 && packageNo > 0)
            {
                cargoStr = cargoStr + string.Format("({0}/{1})", packageNo, packageTotal);
            }

            if (!string.IsNullOrWhiteSpace(to_phone) && to_phone.Length > 3)
            {
                to_phone = to_phone.Substring(0, 3) + (to_phone.Length > 7 ? from_company.Substring(0, 4) + to_phone.Substring(7) : from_company.Substring(0, to_phone.Length - 3));
            }
            if (!string.IsNullOrWhiteSpace(to_mobile) && to_mobile.Length > 3)
            {
                to_mobile = to_mobile.Substring(0, 3) + (to_mobile.Length > 7 ? from_company.Substring(0, 4) + to_mobile.Substring(7) : from_company.Substring(0, to_mobile.Length - 3));
            }

            var routeLabelInfos = waybill.GetJsonObjectValue("routeLabelInfo") as JArray;
            var routeLabelInfo = routeLabelInfos != null && routeLabelInfos.HasValues ? routeLabelInfos[0] as JObject : null;
            var routeLabelData = routeLabelInfo.GetJsonObject("routeLabelData");

            JArray documents = new JArray();
            documents.Add(new JObject()
            {
                { "masterWaybillNo", waybillCode },
                { "seq", 1 },
                { "isPrintLogo", "true" },
                { "isCod", "false" },
                { "isPrintStubLogo", "true" },
                { "payment", "寄付月结" },
                { "productType", "顺丰标快" },
                { "printIcons", new JArray() { "标快" } },
                { "printDateTime", DateTime.Now.ToString("yyyy-MM-dd hh:mm") },
                { "destRouteLabel", routeLabelData.GetJsonObjectStringValue("destRouteLabel") },
                { "destTeamCode", routeLabelData.GetJsonObjectStringValue("destTeamCode") },
                { "codingMapping", routeLabelData.GetJsonObjectStringValue("codingMapping") },
                { "twoDimensionCode", routeLabelData.GetJsonObjectStringValue("twoDimensionCode") },
                { "fromName", from_contact },
                { "fromPhone", from_mobile },
                { "fromAddress", from_address },
                { "toName", to_contact },
                { "toPhone", string.IsNullOrWhiteSpace(to_mobile) ? to_phone : to_mobile },
                { "toAddress", to_address },
                { "remark", cargoStr }
            });

            JObject json = new JObject();
            json.Add("templateCode", templateCode);
            json.Add("fileType", "pdf");
            json.Add("sync", true);
            if (!string.IsNullOrWhiteSpace(version))
                json.Add("version", version);
            json.Add("documents", documents);

            string serviceCode = "COM_RECE_CLOUD_PRINT_WAYBILLS";
            string timestamp = GetTimeStamp(DateTime.Now).ToString();
            string msgData = ToUTF8(json.ToString(Newtonsoft.Json.Formatting.None));
            string msgDigest = Utils.MD5(UrlEncode(msgData + timestamp + appsecret), Encoding.UTF8, false, true);
            string param = string.Format("partnerID={0}&requestID={1}&serviceCode={2}&timestamp={3}&msgData={4}&msgDigest={5}",
                appkey,
                Guid.NewGuid().ToString("N"),
                serviceCode,
                timestamp,
                UrlEncode(msgData),
                msgDigest);

            try
            {
                LogHelper.Info("url: " + url, "SFEApiLog");
                LogHelper.Info("request: " + param, "SFEApiLog");
                var headers = new System.Collections.Generic.Dictionary<string, string>() { { "Content-Type", "application/x-www-form-urlencoded;charset=UTF-8" } };
                string result = HttpHelper.HttpWebRequest(url, param, "POST", timeout, headers, proxy?.ToString());
                LogHelper.Info("response: " + result, "SFEApiLog");

                var responseObject = JSONHelper.JsonToJObject(result);

                string code = responseObject.GetJsonObjectStringValue("apiResultCode");

                if (code.ToUpper() == "A1000")
                {
                    string apiResultData = responseObject.GetJsonObjectStringValue("apiResultData");
                    var apiResultObject = JSONHelper.JsonToJObject(apiResultData);
                    if (!apiResultObject.GetJsonObjectBooleanValue("success"))
                        throw ExceptionHelper.GenException("@E210000004", apiResultObject.GetJsonObjectStringValue("errorMsg"));
                    JObject waybillFileObject = apiResultObject.GetJsonObject("obj");
                    JArray waybillFileList = waybillFileObject.GetJsonObjectValue("files") as JArray;
                    JObject waybillFile = waybillFileList != null && waybillFileList.HasValues ? waybillFileList[0] as JObject : null;

                    if (waybillFile == null)
                        throw new Exception("没有面单数据");
                    string fileUrl = waybillFile.GetJsonObjectStringValue("url");
                    string token = waybillFile.GetJsonObjectStringValue("token");
                    if (string.IsNullOrWhiteSpace(fileUrl))
                        throw new Exception("没有面单PDF路径");
                    if (string.IsNullOrWhiteSpace(token))
                        throw new Exception("没有面单PDF下载token");

                    var downloadHeaders = new System.Collections.Generic.Dictionary<string, string>();
                    downloadHeaders.Add("X-Auth-token", token);
                    byte[] bytes = DownloadPDF(fileUrl, null, null, timeout, downloadHeaders, proxy?.ToString());

                    string fileName = waybillCode + ".pdf";
                    string filePath = waybillFileDir.StartsWith(@"\\") ? waybillFileDir : Utils.GetMapPath(waybillFileDir);
                    if (!Directory.Exists(filePath))
                    {
                        Directory.CreateDirectory(filePath);
                    }
                    string fileFullPath = filePath + "\\" + fileName;
                    FileStream fs = new FileStream(fileFullPath, FileMode.Create);
                    fs.Write(bytes, 0, bytes.Length);
                    fs.Close();

                    using (PdfDocument document = new PdfDocument())
                    {
                        PdfPage page = document.AddPage();
                        page.Size = PdfSharp.PageSize.A4;
                        page.Orientation = PdfSharp.PageOrientation.Landscape;

                        // Get an XGraphics object for drawing
                        XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsUnit.Millimeter);

                        using (XImage image = XImage.FromFile(fileFullPath))
                        {
                            gfx.DrawImage(image, 21, 21, 100, 150);
                            gfx.DrawImage(image, 176, 21, 100, 150);
                        }
                        document.Save(fileFullPath);
                    }

                    if (!string.IsNullOrWhiteSpace(waybillFileUrl) && !waybillFileUrl.EndsWith("/"))
                        waybillFileUrl += "/";

                    var response = new JObject();
                    response["orderId"] = orderId;
                    response["waybillCode"] = waybillCode;
                    response["waybillFilePath"] = fileName;
                    if (!string.IsNullOrWhiteSpace(waybillFileUrl))
                        response["waybillFileURL"] = waybillFileUrl + fileName;
                    return response;
                }
                else
                {
                    string msg = responseObject.GetJsonObjectStringValue("apiErrorMsg");
                    throw ExceptionHelper.GenException("@E210000004", msg);
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng CreateWaybillPDFNew fail", ex, "SFEApiLog");
                throw new Exception("@E210000003");
            }
        }

        public JObject SearchWaybill(JObject request, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string url = config.GetNecessaryJsonObjectStringValue("url", path);
            string appkey = config.GetNecessaryJsonObjectStringValue("appkey", path);
            string appsecret = config.GetNecessaryJsonObjectStringValue("appsecret", path);
            var timeout = config.GetJsonObjectIntValue("timeout", 30000);
            var proxy = config.GetJsonObjectValue("proxy");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");
            string deliverOrderId = request.GetJsonObjectStringValue("deliverOrderId", orderId);

            StringBuilder xml = new StringBuilder();
            xml.Append("<?xml version='1.0' encoding='UTF-8'?>");
            xml.Append("<Request service='OrderSearchService' lang='zh-CN'>");
            xml.AppendFormat("<Head>{0}</Head>", appkey);
            xml.Append("<Body>");
            xml.AppendFormat("<OrderSearch orderid='{0}'>", XmlEscape(deliverOrderId));
            xml.Append("</OrderSearch>");
            xml.Append("</Body>");
            xml.Append("</Request>");

            string utf8String = ToUTF8(xml.ToString());
            string verifyCode = Utils.MD5(utf8String + appsecret, Encoding.UTF8, false, true);
            string param = string.Format("xml={0}&verifyCode={1}", System.Web.HttpUtility.UrlEncode(utf8String), System.Web.HttpUtility.UrlEncode(verifyCode));
            
            try
            {
                LogHelper.Info("url: " + url, "SFEApiLog");
                LogHelper.Info("request: " + param, "SFEApiLog");
                string result = HttpHelper.HttpWebRequest(url, param, "POST", timeout, null, proxy?.ToString());
                LogHelper.Info("response: " + result, "SFEApiLog");

                XmlDocument doc = new XmlDocument();
                doc.LoadXml(result);

                XmlNode Head = doc.SelectSingleNode("/Response/Head");

                string status = Head.InnerText;

                if (status.ToUpper() == "OK")
                {
                    JObject waybill = new JObject();
                    XmlNode OrderResponse = doc.SelectSingleNode("/Response/Body/OrderResponse");
                    waybill["origincode"] = OrderResponse.Attributes["origincode"] != null ? OrderResponse.Attributes["origincode"].Value : string.Empty;
                    waybill["destcode"] = OrderResponse.Attributes["destcode"] != null ? OrderResponse.Attributes["destcode"].Value : string.Empty;
                    waybill["waybillCode"] = OrderResponse.Attributes["mailno"] != null ? OrderResponse.Attributes["mailno"].Value : string.Empty;

                    JObject response = new JObject();
                    response["orderId"] = orderId;
                    response["waybillCode"] = waybill["waybillCode"];
                    response["waybillFilePath"] = CreateWaybillPDF(request, waybill, config).GetJsonObjectStringValue("waybillFilePath");
                    return response;
                }
                else
                {
                    //XmlNode ERROR = doc.SelectSingleNode("/Response/ERROR");
                    //throw ExceptionHelper.GenException("@E210000002", ERROR.InnerText);
                    return null;
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng SearchWaybill fail", ex, "SFEApiLog");
                throw new Exception("@E210000001");
            }
        }

        public JObject CreateWaybill(JObject request, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string url = config.GetNecessaryJsonObjectStringValue("url", path);
            string appkey = config.GetNecessaryJsonObjectStringValue("appkey", path);
            string appsecret = config.GetNecessaryJsonObjectStringValue("appsecret", path);
            string customerCode = config.GetNecessaryJsonObjectStringValue("customercode", path);
            string expressType = config.GetJsonObjectStringValue("expresstype", "2");
            string payMethod = config.GetJsonObjectStringValue("paymethod", "1");
            string isDoCall = config.GetJsonObjectStringValue("isdocall", "1");
            var timeout = config.GetJsonObjectIntValue("timeout", 30000);
            var proxy = config.GetJsonObjectValue("proxy");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");
            string deliverOrderId = request.GetJsonObjectStringValue("deliverOrderId", orderId);

            StringBuilder xml = new StringBuilder();
            xml.Append("<?xml version='1.0' encoding='UTF-8'?>");
            xml.Append("<Request service='OrderService' lang='zh-CN'>");
            xml.AppendFormat("<Head>{0}</Head>", appkey);
            xml.Append("<Body>");
            xml.Append("<Order ");
            xml.AppendFormat("orderid='{0}' ", XmlEscape(deliverOrderId));
            xml.AppendFormat("j_company='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_company")));
            xml.AppendFormat("j_contact='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_contact")));
            xml.AppendFormat("j_tel='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_phone")));
            xml.AppendFormat("j_mobile='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_mobile")));
            xml.AppendFormat("j_province='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_province")));
            xml.AppendFormat("j_city='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_city")));
            xml.AppendFormat("j_county='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_county")));
            xml.AppendFormat("j_address='{0}' ", XmlEscape(request.GetJsonObjectStringValue("from_address")));
            xml.AppendFormat("d_company='{0}' ", XmlEscape(request.GetJsonObjectStringValue("to_company")));
            xml.AppendFormat("d_contact='{0}' ", XmlEscape(request.GetJsonObjectStringValue("to_contact")));
            xml.AppendFormat("d_tel='{0}' ", XmlEscape(request.GetJsonObjectStringValue("to_phone")));
            xml.AppendFormat("d_mobile='{0}' ", XmlEscape(request.GetJsonObjectStringValue("to_mobile")));
            xml.AppendFormat("d_address='{0}' ", XmlEscape(request.GetJsonObjectStringValue("to_address")));
            xml.AppendFormat("express_type='{0}' ", XmlEscape(expressType));
            xml.AppendFormat("pay_method='{0}' ", XmlEscape(payMethod));
            xml.AppendFormat("is_docall='{0}' ", XmlEscape(isDoCall));
            xml.AppendFormat("custid='{0}' ", XmlEscape(customerCode));
            xml.Append("parcel_quantity='1' ");
            xml.AppendFormat("remark='{0}' ", XmlEscape(request.GetJsonObjectStringValue("remark")));
            xml.Append(">");
            var items = request.GetJsonObjectList<JObject>("cargoes");
            if (items != null)
            {
                foreach (var item in items)
                {
                    xml.AppendFormat("<Cargo name='{0}' count='{1}'></Cargo>", XmlEscape(item.GetJsonObjectStringValue("sku")), item.GetJsonObjectIntValue("qty"));
                }
            }
            xml.Append("</Order>");
            xml.Append("</Body>");
            xml.Append("</Request>");

            string utf8String = ToUTF8(xml.ToString());
            string verifyCode = Utils.MD5(utf8String + appsecret, Encoding.UTF8, false, true);
            string param = string.Format("xml={0}&verifyCode={1}", System.Web.HttpUtility.UrlEncode(utf8String), System.Web.HttpUtility.UrlEncode(verifyCode));
            
            try
            {
                LogHelper.Info("url: " + url, "SFEApiLog");
                LogHelper.Info("request: " + param, "SFEApiLog");
                string result = HttpHelper.HttpWebRequest(url, param, "POST", timeout, null, proxy?.ToString());
                LogHelper.Info("response: " + result, "SFEApiLog");
                
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(result);

                XmlNode Head = doc.SelectSingleNode("/Response/Head");

                string status = Head.InnerText;

                if (status.ToUpper() == "OK")
                {
                    JObject waybill = new JObject();
                    XmlNode OrderResponse = doc.SelectSingleNode("/Response/Body/OrderResponse");
                    waybill["origincode"] = OrderResponse.Attributes["origincode"] != null ? OrderResponse.Attributes["origincode"].Value : string.Empty;
                    waybill["destcode"] = OrderResponse.Attributes["destcode"] != null ? OrderResponse.Attributes["destcode"].Value : string.Empty;
                    waybill["waybillCode"] = OrderResponse.Attributes["mailno"] != null ? OrderResponse.Attributes["mailno"].Value : string.Empty;

                    JObject response = new JObject();
                    response["orderId"] = orderId;
                    response["waybillCode"] = waybill["waybillCode"];
                    response["waybillFilePath"] = CreateWaybillPDF(request, waybill, config).GetJsonObjectStringValue("waybillFilePath");
                    return response;
                }
                else
                {
                    XmlNode ERROR = doc.SelectSingleNode("/Response/ERROR");
                    throw ExceptionHelper.GenException("@E210000002", ERROR.InnerText);
                }
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng CreateWaybill fail", ex, "SFEApiLog");
                throw new Exception("@E210000001");
            }
        }

        public JObject CreateWaybillPDF(JObject request, JObject waybill, JObject config)
        {
            string path = string.IsNullOrWhiteSpace(courierCode) ? null : string.Format("constant.courierInfo.{0}", courierCode);
            string templatePath = config.GetNecessaryJsonObjectStringValue("templatepath", path);
            string waybillFileDir = config.GetNecessaryJsonObjectStringValue("waybillfiledir", path);
            string waybillFileUrl = config.GetJsonObjectStringValue("waybillfileurl");
            string customerCode = config.GetJsonObjectStringValue("customercode");
            string expressType = config.GetJsonObjectStringValue("expresstype", "2");
            string payMethod = config.GetJsonObjectStringValue("paymethod", "1");

            int packageNo = request.GetJsonObjectIntValue("package_no");
            int packageTotal = request.GetJsonObjectIntValue("package_total");
            string ecomOrderId = request.GetJsonObjectStringValue("ecom_orderid");
            string orderId = request.GetNecessaryJsonObjectStringValue("orderid");

            string from_company = "******";
            string from_contact = request.GetJsonObjectStringValue("from_contact");
            string from_phone = null;
            string from_mobile = null;
            string from_province = request.GetJsonObjectStringValue("from_province");
            string from_city = request.GetJsonObjectStringValue("from_city");
            string from_county = request.GetJsonObjectStringValue("from_county");
            string from_address = null;
            string to_company = request.GetJsonObjectStringValue("to_company");
            string to_contact = request.GetJsonObjectStringValue("to_contact");
            string to_phone = request.GetJsonObjectStringValue("to_phone");
            string to_mobile = request.GetJsonObjectStringValue("to_mobile");
            string to_address = request.GetJsonObjectStringValue("to_address");

            string originCode = waybill.GetNecessaryJsonObjectStringValue("origincode");
            string destCode = waybill.GetNecessaryJsonObjectStringValue("destcode");
            string waybillCode = waybill.GetNecessaryJsonObjectStringValue("waybillCode");

            string cargoStr = "";
            var items = request.GetJsonObjectList<JObject>("cargoes");
            if (items != null)
            {
                foreach (var item in items)
                {
                    cargoStr += string.Format("{0}*{1};", item.GetJsonObjectStringValue("sku"), item.GetJsonObjectIntValue("qty"));
                }
            }
            cargoStr = cargoStr + ecomOrderId;

            // 在remark显示(多包裹)
            if (packageTotal > 1 && packageNo > 0)
            {
                cargoStr = cargoStr + string.Format("({0}/{1})", packageNo, packageTotal);
            }

            if (!string.IsNullOrWhiteSpace(to_phone) && to_phone.Length > 3)
            {
                to_phone = to_phone.Substring(0, 3) + (to_phone.Length > 7 ? from_company.Substring(0, 4) + to_phone.Substring(7) : from_company.Substring(0, to_phone.Length - 3));
            }
            if (!string.IsNullOrWhiteSpace(to_mobile) && to_mobile.Length > 3)
            {
                to_mobile = to_mobile.Substring(0, 3) + (to_mobile.Length > 7 ? from_company.Substring(0, 4) + to_mobile.Substring(7) : from_company.Substring(0, to_mobile.Length - 3));
            }

            //装载报表
            LocalReport localReport = new LocalReport();
            localReport.EnableExternalImages = true;
            localReport.ReportPath = Utils.GetMapPath(templatePath);

            ReportParameter orderid = new ReportParameter("orderid", orderId);
            ReportParameter j_company = new ReportParameter("j_company", from_company);
            ReportParameter j_contact = new ReportParameter("j_contact", from_contact);
            ReportParameter j_tel = new ReportParameter("j_tel", from_phone);
            ReportParameter j_mobile = new ReportParameter("j_mobile", from_mobile);
            ReportParameter j_province = new ReportParameter("j_province", from_province);
            ReportParameter j_city = new ReportParameter("j_city", from_city);
            ReportParameter j_county = new ReportParameter("j_county", from_county);
            ReportParameter j_address = new ReportParameter("j_address", from_address);
            ReportParameter d_company = new ReportParameter("d_company", to_company);
            ReportParameter d_contact = new ReportParameter("d_contact", to_contact);
            ReportParameter d_tel = new ReportParameter("d_tel", to_phone);
            ReportParameter d_mobile = new ReportParameter("d_mobile", to_mobile);
            ReportParameter d_address = new ReportParameter("d_address", to_address);
            ReportParameter pay_method = new ReportParameter("pay_method", payMethod);
            ReportParameter remark = new ReportParameter("remark", cargoStr);
            ReportParameter destcode = new ReportParameter("destcode", destCode);
            ReportParameter mailno = new ReportParameter("mailno", waybillCode);
            ReportParameter origincode = new ReportParameter("origincode", originCode);
            ReportParameter cargoes = new ReportParameter("cargoes", cargoStr);
            ReportParameter express_type = new ReportParameter("express_type", expressType);
            ReportParameter custid = new ReportParameter("custid", customerCode);

            ReportParameter subOrderSeq = new ReportParameter("subOrderSeq", packageNo.ToString());
            ReportParameter totalSubOrders = new ReportParameter("totalSubOrders", packageTotal.ToString());

            localReport.SetParameters(subOrderSeq);
            localReport.SetParameters(totalSubOrders);


            localReport.SetParameters(orderid);
            localReport.SetParameters(j_company);
            localReport.SetParameters(j_contact);
            localReport.SetParameters(j_tel);
            localReport.SetParameters(j_mobile);
            localReport.SetParameters(j_province);
            localReport.SetParameters(j_city);
            localReport.SetParameters(j_county);
            localReport.SetParameters(j_address);
            localReport.SetParameters(d_company);
            localReport.SetParameters(d_contact);
            localReport.SetParameters(d_tel);
            localReport.SetParameters(d_mobile);
            localReport.SetParameters(d_address);
            localReport.SetParameters(pay_method);
            localReport.SetParameters(remark);
            localReport.SetParameters(destcode);
            localReport.SetParameters(mailno);
            localReport.SetParameters(origincode);
            localReport.SetParameters(cargoes);
            localReport.SetParameters(express_type);
            localReport.SetParameters(custid);

            try
            {
                Image image = null;
                try
                {
                    image = new BarCode().GetCodeImage(waybillCode, BarCode.Encode.Code128C);
                }
                catch
                {
                    image = new BarCode().GetCodeImage(waybillCode, BarCode.Encode.Code128A);
                }
                //装载条码图片
                DataTable dt = new DataTable();
                dt.Columns.Add("BarCode", Type.GetType("System.Byte[]"));
                DataRow newRow = dt.NewRow();
                newRow["BarCode"] = ConvertJpegImageToBytes(image);
                dt.Rows.Add(newRow);

                ReportDataSource rds = new ReportDataSource("DataSet1", dt);
                localReport.DataSources.Clear();
                localReport.DataSources.Add(rds);


                //生成PDF并保存到服务器上
                Warning[] warnings;
                string[] streamids;
                string mimeType;
                string encoding;
                string extension;

                byte[] bytes = localReport.Render("PDF", null, out mimeType, out encoding, out extension, out streamids, out warnings);

                string fileName = waybillCode + ".pdf";
                string filePath = waybillFileDir.StartsWith(@"\\") ? waybillFileDir : Utils.GetMapPath(waybillFileDir);

                if (!Directory.Exists(filePath))
                {
                    Directory.CreateDirectory(filePath);
                }
                string fileFullPath = filePath + "\\" + fileName;
                FileStream fs = new FileStream(fileFullPath, FileMode.Create);
                fs.Write(bytes, 0, bytes.Length);
                fs.Close();

                if (!string.IsNullOrWhiteSpace(waybillFileUrl) && !waybillFileUrl.EndsWith("/"))
                    waybillFileUrl += "/";

                var response = new JObject();
                response["orderId"] = orderId;
                response["waybillCode"] = waybillCode;
                response["waybillFilePath"] = fileName;
                if (!string.IsNullOrWhiteSpace(waybillFileUrl))
                    response["waybillFileURL"] = waybillFileUrl + fileName;
                return response;
            }
            catch (Exception ex)
            {
                LogHelper.Error("ShunFeng CreateWaybillPDF fail", ex, "SFEApiLog");
                throw new Exception("@E210000003");
            }
        }

        private static string XmlEscape(string s)
        {
            if (string.IsNullOrWhiteSpace(s)) return s;
            s = s.Replace("&", "&amp;");
            s = s.Replace("<", "&lt;");
            s = s.Replace("'", "&apos;");
            s = s.Replace(">", "&gt;");
            s = s.Replace("\"", "&quot;");
            return s;
        }

        public static string ToUTF8(string unicodeString)
        {
            UTF8Encoding utf8 = new UTF8Encoding();
            Byte[] encodedBytes = utf8.GetBytes(unicodeString);
            String decodedString = utf8.GetString(encodedBytes);
            return decodedString;
        }

        private static byte[] ConvertJpegImageToBytes(Image image)
        {
            if (image == null)
            {
                return null;
            }

            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Jpeg);

            byte[] bytes = ms.GetBuffer();

            return bytes;
        }

        private static string UrlEncode(string str)
        {
            StringBuilder builder = new StringBuilder();
            foreach (char c in str)
            {
                if (c == '(')
                {
                    builder.Append("%28");
                }
                else if (c == ')')
                {
                    builder.Append("%29");
                }
                else
                {
                    string encoded = System.Web.HttpUtility.UrlEncode(c.ToString(), Encoding.UTF8);
                    if (encoded.Length > 1)
                    {
                        builder.Append(encoded.ToUpper());
                    }
                    else
                    {
                        builder.Append(c);
                    }
                }
            }
            return builder.ToString();
        }

        private static long GetTimeStamp(DateTime datetime)
        {
            if (datetime.Kind != DateTimeKind.Utc)
                datetime = datetime.ToUniversalTime();
            var epochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var span = (long)((datetime - epochTime).TotalMilliseconds);
            return span;
        }

        private static byte[] DownloadPDF(string url, string param = null, string method = null, int timeout = 0, System.Collections.Generic.Dictionary<string, string> headers = null, string proxy = null, bool ignoreSSLCheck = true)
        {
            if (string.IsNullOrWhiteSpace(url))
                throw new ArgumentNullException("url");

            if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
            {
                System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12;
                if (ignoreSSLCheck)
                    System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback((object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors) => true);
            }
            System.Net.HttpWebRequest req = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(url);
            if (headers != null && headers.Count > 0)
            {
                foreach (var item in headers)
                {
                    if (item.Key.Equals("user-agent", StringComparison.OrdinalIgnoreCase))
                        req.UserAgent = item.Value;
                    else if (item.Key.Equals("content-type", StringComparison.OrdinalIgnoreCase))
                        req.ContentType = item.Value;
                    else if (item.Key.Equals("host", StringComparison.OrdinalIgnoreCase))
                        req.Host = item.Value;
                    else if (item.Key.Equals("accept", StringComparison.OrdinalIgnoreCase))
                        req.Accept = item.Value;
                    else if (item.Key.Equals("referer", StringComparison.OrdinalIgnoreCase))
                        req.Referer = item.Value;
                    else
                        req.Headers[item.Key] = item.Value;
                }
            }
            req.Method = !string.IsNullOrWhiteSpace(method) ? method.ToUpper() : "GET";
            if (req.Method.Equals("POST") && string.IsNullOrWhiteSpace(req.ContentType))
                req.ContentType = "application/x-www-form-urlencoded";
            req.Timeout = timeout > 0 ? timeout : 5000;
            req.ContentLength = 0;
            StreamReader reader = null;
            System.Net.HttpWebResponse resp = null;
            try
            {
                if (!string.IsNullOrWhiteSpace(proxy))
                {
                    if (proxy.ToLower() == "false")
                        req.Proxy = null;
                    else
                    {
                        int port;
                        string[] hostAndPort = proxy.Split(':');
                        if (hostAndPort.Length == 2 && int.TryParse(hostAndPort[1], out port))
                            req.Proxy = new System.Net.WebProxy(hostAndPort[0], port);
                    }
                }
                if (!string.IsNullOrWhiteSpace(param))
                {
                    byte[] data = Encoding.UTF8.GetBytes(param);
                    req.ContentLength = data.Length;
                    using (Stream stream = req.GetRequestStream())
                    {
                        stream.Write(data, 0, data.Length);     //写入数据
                    }
                }
                byte[] dataBytes = new byte[0];
                resp = (System.Net.HttpWebResponse)req.GetResponse();
                using (Stream dataStream = resp.GetResponseStream())
                {
                    int readCount = 0;
                    int bufferSize = 8192;
                    var buffer = new byte[bufferSize];
                    using (var memory = new MemoryStream())
                    {
                        while ((readCount = dataStream.Read(buffer, 0, bufferSize)) > 0)
                        {
                            memory.Write(buffer, 0, readCount);
                        }

                        dataBytes = memory.ToArray();
                    }
                }
                return dataBytes;
            }
            catch (System.Threading.ThreadAbortException e)
            {
                System.Threading.Thread.ResetAbort();
                throw e;
            }
            catch (Exception e)
            {
                throw e;
            }
            finally
            {
                if (reader != null)
                    reader.Close();
                if (resp != null)
                    resp.Close();
                req.Abort();
            }
        }
    }
}
